#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Socket.hpp"

using namespace socket_ns;

class EpollServer
{
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildTcpServerSocket(port);
        _epfd = ::epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "Epoll create error!\n");
            exit(1);
        }
        LOG(INFO, "Epoll create success, epollfd: %d\n", _epfd);
    }

    void Init()
    {
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock->Sockfd();
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "Epoll_ctl error!\n");
            exit(2);
        }
        LOG(INFO, "Epoll_ctl success, add new sockfd: %d\n", _listensock->Sockfd());
    }

    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listensock->Accepter(&addr);
        if (sockfd < 0)
        {
            LOG(ERROR, "Failed to get new link!\n");
            return;
        }
        LOG(INFO, "Get a new link, client info: %s:%d\n", addr.ip().c_str(), addr.port());

        // 将新sockfd添加到epoll中进行监控
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sockfd;
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (n < 0)
        {
            LOG(FATAL, "Epoll_ctl error!\n");
            return;
        }
        LOG(INFO, "Epoll_ctl success, add new sockfd: %d\n", sockfd);
    }

    void HandlerIO(int fd)
    {
        char buffer[1024];
        ssize_t n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say# " << buffer << std::endl;
            std::string content = "<html><body><h1>hello epoll</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            ::send(fd, echo_str.c_str(), echo_str.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit.\n");
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
        else
        {
            LOG(INFO, "recv error!\n");
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
    }

    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            int fd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;
            LOG(INFO, "\n\n");
            if (events & EPOLLIN)
            {
                // 读事件就绪，新连接到来
                if (fd == _listensock->Sockfd())
                    Accepter();
                else
                    HandlerIO(fd);
            }
        }
    }

    void Loop()
    {
        int timeout = 1000;
        while (true)
        {
            int n = ::epoll_wait(_epfd, _revs, num, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "epoll time out...\n");
                break;
            case -1:
                LOG(ERROR, "epoll error\n");
                break;
            default:
                LOG(INFO, "haved event happend!, n : %d\n", n);
                HandlerEvent(n);
                break;
            }
        }
    }

    ~EpollServer()
    {
        if (_epfd >= 0)
            ::close(_epfd);
        _listensock->Close();
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
    int _epfd;
    struct epoll_event _revs[num];
};